﻿using BattleFury.Lib.Bots;
using BattleFury.Lib.Network;
using BattleFury.Loader.Commands;
using BattleFury.Loader.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Xml.Serialization;

namespace BattleFury.Loader.ViewModels
{
    public class MainViewModel
        : DependencyObject
    {
        #region Static Properties

        public static string BotPath = ".\\Bots";
        public static string ClientPath = ".\\Client";
        public static string ServerPath = ".\\Server";
        public static string ClientConfigPath = ClientPath + "\\BattleFury.config";

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty CreateClientCommandProperty = DependencyProperty.Register("CreateClientCommand", typeof(ICommand), typeof(MainViewModel));
        public ICommand CreateClientCommand
        {
            get { return (ICommand)GetValue(CreateClientCommandProperty); }
            set { SetValue(CreateClientCommandProperty, value); }
        }

        public static readonly DependencyProperty CreateServerCommandProperty = DependencyProperty.Register("CreateServerCommand", typeof(ICommand), typeof(MainViewModel));
        public ICommand CreateServerCommand
        {
            get { return (ICommand)GetValue(CreateServerCommandProperty); }
            set { SetValue(CreateServerCommandProperty, value); }
        }

        public static readonly DependencyProperty AddBotCommandProperty = DependencyProperty.Register("AddBotCommand", typeof(ICommand), typeof(MainViewModel));
        public ICommand AddBotCommand
        {
            get { return (ICommand)GetValue(AddBotCommandProperty); }
            set { SetValue(AddBotCommandProperty, value); }
        }

        public static readonly DependencyProperty RemoveBotCommandProperty = DependencyProperty.Register("RemoveBotCommand", typeof(ICommand), typeof(MainViewModel));
        public ICommand RemoveBotCommand
        {
            get { return (ICommand)GetValue(RemoveBotCommandProperty); }
            set { SetValue(RemoveBotCommandProperty, value); }
        }

        public static readonly DependencyProperty UsernameProperty = DependencyProperty.Register("Username", typeof(string), typeof(MainViewModel));
        public string Username
        {
            get { return (string)GetValue(UsernameProperty); }
            set { SetValue(UsernameProperty, value); }
        }

        public static readonly DependencyProperty IsSpectatorProperty = DependencyProperty.Register("IsSpectator", typeof(bool), typeof(MainViewModel));
        public bool IsSpectator
        {
            get { return (bool)GetValue(IsSpectatorProperty); }
            set { SetValue(IsSpectatorProperty, value); }
        }

        public static readonly DependencyProperty HostProperty = DependencyProperty.Register("Host", typeof(string), typeof(MainViewModel));
        public string Host
        {
            get { return (string)GetValue(HostProperty); }
            set { SetValue(HostProperty, value); }
        }

        public static readonly DependencyProperty PortProperty = DependencyProperty.Register("Port", typeof(string), typeof(MainViewModel));
        public string Port
        {
            get { return (string)GetValue(PortProperty); }
            set { SetValue(PortProperty, value); }
        }

        public static readonly DependencyProperty LoadedBotsProperty = DependencyProperty.Register("LoadedBots", typeof(ObservableCollection<BotModel>), typeof(MainViewModel));
        public ObservableCollection<BotModel> LoadedBots
        {
            get { return (ObservableCollection<BotModel>)GetValue(LoadedBotsProperty); }
            set { SetValue(LoadedBotsProperty, value); }
        }

        public static readonly DependencyProperty SelectedBotsProperty = DependencyProperty.Register("SelectedBots", typeof(ObservableCollection<BotModel>), typeof(MainViewModel));
        public ObservableCollection<BotModel> SelectedBots
        {
            get { return (ObservableCollection<BotModel>)GetValue(SelectedBotsProperty); }
            set { SetValue(SelectedBotsProperty, value); }
        }

        public static readonly DependencyProperty SelectedBotProperty = DependencyProperty.Register("SelectedBot", typeof(BotModel), typeof(MainViewModel));
        public BotModel SelectedBot
        {
            get { return (BotModel)GetValue(SelectedBotProperty); }
            set { SetValue(SelectedBotProperty, value); }
        }

        public static readonly DependencyProperty SelectedLoadedBotProperty = DependencyProperty.Register("SelectedLoadedBot", typeof(BotModel), typeof(MainViewModel));
        public BotModel SelectedLoadedBot
        {
            get { return (BotModel)GetValue(SelectedLoadedBotProperty); }
            set { SetValue(SelectedLoadedBotProperty, value); }
        }


        #endregion

        #region Initialization

        public MainViewModel()
        {
            Username = "Player";
            Host = "localhost";
            Port = "28657";
            IsSpectator = true;

            SelectedBots = new ObservableCollection<BotModel>();
            LoadedBots = new ObservableCollection<BotModel>();

            RegisterCommands();

            GetBotsFromDirectory();
        }

        private void RegisterCommands()
        {
            CreateClientCommand = new DelegateCommand(CreateClient);
            CreateServerCommand = new DelegateCommand(CreateServer);
            AddBotCommand = new DelegateCommand(AddBot);
            RemoveBotCommand = new DelegateCommand(RemoveBot);
        }

        #endregion

        #region Actions

        public void CreateClient(object sender = null)
        {
            CreateClientConfiguration();
            Process.Start(ClientPath + "\\BattleFury.exe");
        }

        public void CreateServer(object sender = null)
        {
            Process.Start(ServerPath + "\\BattleFury.Server.Console.exe");
        }

        public void AddBot(object sender = null)
        {
            if (SelectedLoadedBot == null) return;
            SelectedBots.Add(SelectedLoadedBot);
        }

        public void RemoveBot(object sender = null)
        {
            if (SelectedBot == null) return;
            SelectedBots.Remove(SelectedBot);
        }

        #endregion

        #region Methods

        public void GetBotsFromDirectory()
        {
            if (!Directory.Exists(BotPath)) return;
            var libs = Directory.GetFiles(BotPath, "*.dll");

            var bots = new List<BotModel>();
            foreach (var lib in libs)
            {
                bots.AddRange(GetBotsFromLibrary(lib));
            }

            LoadedBots = new ObservableCollection<BotModel>(bots);
        }

        private IEnumerable<BotModel> GetBotsFromLibrary(string path)
        {
            var types = Assembly.LoadFrom(path).GetTypes();

            var bots = new List<BotModel>();
            foreach (var type in types)
            {
                var isIBot = (from i in type.GetInterfaces() select i.Name).ToList().Any(i => i.Contains(typeof(Bot).Name));

                if (isIBot && type.Name == typeof(Bot).Name)
                    isIBot = false;
                if (isIBot)
                {
                    var model = new BotModel
                    {
                        Name = type.Name,
                        FullName = type.FullName,
                        Path = Path.GetFullPath(path)
                    };
                    bots.Add(model);
                }
            }
            return bots;
        }

        private void CreateClientConfiguration()
        {
            var config = new ClientConfiguration
            {
                Username = Username,
                Host = Host,
                Port = Convert.ToInt32(Port),
                IsSpectator = IsSpectator,
                Bots = new List<BotConfiguration>(),
            };
            foreach (var bot in SelectedBots)
            {
                config.Bots.Add(new BotConfiguration
                {
                    Name = bot.Name,
                    FullName = bot.FullName,
                    Path = bot.Path,
                });
            }

            if (File.Exists(ClientConfigPath)) File.Delete(ClientConfigPath);

            var serializer = new XmlSerializer(typeof(ClientConfiguration));
            using (var writer = new StreamWriter(ClientConfigPath))
            {
                serializer.Serialize(writer, config);
            }
        }

        #endregion
    }
}